package com.ssky.bean.validator;


import com.ssky.bean.validator.core.Constant;
import com.ssky.bean.validator.core.FieldValidator;
import com.ssky.bean.validator.core.exception.VldException;
import com.ssky.bean.validator.core.rule.*;
import com.ssky.bean.validator.util.ReadUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.util.CollectionUtils;

import java.io.IOException;
import java.util.*;

/**
 * 规则文件加载器
 */
public class ValidatorLoader {
    private static final Logger L = LoggerFactory.getLogger(ValidatorLoader.class);
    /* 规则文件内容的hashcode，可用于判断同一文件，内容是否发生变化 */
    private final Map<String, String> ruleConfigContentHashcodeMap = new HashMap<>();

    private final IValidatorRulesParser validatorRulesParser;

    private final IValidatorRuleDefiner validatorRuleDefiner;


    public ValidatorLoader() {
        validatorRuleDefiner = new ValidatorRuleDefiner();
        validatorRulesParser = new ValidatorRulesParser();
    }

    public ValidatorLoader(IValidatorRulesParser validatorRulesParser, IValidatorRuleDefiner validatorRuleDefiner) {
        this.validatorRulesParser = validatorRulesParser;
        this.validatorRuleDefiner = validatorRuleDefiner;
    }

    public void load(String localtionPattern) throws VldException {
         this.load(localtionPattern, null);
    }

    /**
     * @param localtionPattern 规则文件匹配路径
     * @param validatorIdSet   仅加载指定validatorId；为null加载全部
     * @return
     * @throws VldException
     */
    public void load(String localtionPattern, Set<String> validatorIdSet) throws VldException {

        List<ValidatorRules> validatorRulesList = findLoadValidatorRules(localtionPattern);

        List<ValidatorRule> validatorRuleList = new ArrayList<>();
        for (ValidatorRules rules : validatorRulesList) {
            List<ValidatorRule> vRuleList = rules.getValidatorRuleList();
            if (CollectionUtils.isEmpty(vRuleList)) {
                continue;
            } else {
                for (ValidatorRule vldtorRule : vRuleList) {
                    if (!CollectionUtils.isEmpty(validatorIdSet) && validatorIdSet.contains(vldtorRule.getId())) {
                        validatorRuleList.add(vldtorRule);//仅加载指定id集合中的
                    } else {
                        validatorRuleList.add(vldtorRule);
                    }
                }
            }
        }
        List<ValidatorRule> validatorRuleLs = validatorRuleDefiner.define(validatorRuleList);

        if (!CollectionUtils.isEmpty(validatorRuleLs)) {
            for (ValidatorRule vldRule : validatorRuleLs) {
                ValidatorBuilder builder = new ValidatorBuilder();
                FieldValidator feildValidator = null;
                try {
                    feildValidator = builder.build(vldRule);
                } catch (Exception e) {
                    L.error("builder.build(vldRule) occured exception -->", e);
                    throw new VldException(e.getMessage());
                }
                String[] vldIdRealArray = vldRule.getId().split(";");
                // 构造完毕后，最后进行id映射验证器，避免重复创建。
                for (String vldId : vldIdRealArray) {
                    FieldValidatorCache.getSingleInstance().put(vldId, feildValidator);
                }
            }
        }
    }

    /**
     * 查找并加载规则文件，将规文件解析为原始形态实体对象结构。
     * 职责：1.根据classpath路径扫描文件；2.规则文件解析为原始形态实体对象结构。
     *
     * @param localtionPattern
     * @return
     * @throws VldException
     */
    public List<ValidatorRules> findLoadValidatorRules(String localtionPattern) throws VldException {
        Resource[] resources = scanConfigRule(localtionPattern);
        List<ValidatorRules> validatorRulesList = new ArrayList<>();
        if (resources == null) {
            return validatorRulesList;
        }
        for (Resource res : resources) {
            ValidatorRules validRules = null;
            try {
                String fileKey = res.getFile().getAbsolutePath();
                L.info("ValidatorRuleLoader --> validatorRule resources path:" + fileKey);
                String fileContent = ReadUtil.getInstance().readAll(res.getInputStream(), Constant.Charset_Utf8);
                String fileContentHashcode = String.valueOf(fileContent.hashCode());
                if (this.ruleConfigContentHashcodeMap.containsKey(fileKey) &&
                        fileContentHashcode.equals(this.ruleConfigContentHashcodeMap.get(fileKey))) {
                    L.info("ValidatorRuleLoader -->  validatorRule file = " + fileKey + " is no change, don‘t need to load!");
                } else {
                    L.info("ValidatorRuleLoader -->  validatorRule file = " + fileKey + " need to be load!");
                    this.ruleConfigContentHashcodeMap.put(fileKey, fileContentHashcode);
                    validRules = validatorRulesParser.parse(fileContent);
                    validRules.setIdentifier(fileContentHashcode);
                    validatorRulesList.add(validRules);
                }
            } catch (IOException e) {
                throw new VldException("ValidatorRuleLoader read occured ioException:" + e.getMessage());
            }
        }
        return validatorRulesList;
    }

    public Resource[] scanConfigRule(String localtionPattern) {
        L.info("ValidatorRuleLoader --> Scan validatorRuleConfigPath = " + localtionPattern);
        List<ValidatorRules> validatorRulesList = new ArrayList<>();
        PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
        Resource[] resources = null;
        try {
            resources = resolver.getResources(localtionPattern);
        } catch (IOException e1) {
            L.error("ValidatorRuleLoader --> Scan validatorRuleConfigPath = " + localtionPattern + " occur error-->", e1);
            throw new VldException(e1.getMessage());
        }
        resolver = null;
        return resources;
    }
}
